/**
 * java is a big-debian.
 * tcp/ip net byte-order is big-debian
 * 
 * processor         os     byte-order
 * Alpha             ALL    Little endian
 * HP-PA             NT     Little endian
 * HP-PA             UNIX   Big endian
 * Intelx86          ALL    Little endian <-----x86 is little endian
 * Motorola680x()    ALL    Big endian
 * MIPS              NT     Little endian
 * MIPS              UNIX   Big endian
 * PowerPC           NT     Little endian
 * PowerPC           No NT  Big endian  <-----PPC is big endian
 * RS/6000           UNIX   Big endian
 * SPARC             UNIX   Big endian
 * IXP1200 ARM core  ALL    Little endian 
 *
 */
package com.dd.keel.core.common.utils;


import java.lang.StringBuilder;
import java.util.Arrays;


/**
 * java basic number types:
 * long => 64 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 
 * int  => 32 -2,147,483,648 ~ 2,147,483,647 
 * short=> 16 -32,768 ~ 32,767 
 * byte =>  8 -128~127 
 * @author Kevin.XU
 *
 */

public class ByteUtil {
	
	private static char[] hex_chars = new char[]{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 
	
	private static int ONE_BYTE_MASK = 0XFF;
	
	public static void fillWithNUL(byte[] b)
	{
		for(int i=0; i<b.length; ++i)
		{
			b[i] = 0x00;
		}
	}
	
	public static byte[] reverse(byte[] b)
	{
		byte[] b1 = new byte[b.length];
		int index = 0;
		for(int i=b.length-1; i>=0; --i)
		{
			b1[index] = b[i];
			++index;
		}
		return b1;
	}
	
	public static boolean byteArrayEquals(byte[] array1,int array1off,byte[] array2,int array2off,int len)
	{
	    for (int i = 0; i < len; ++i)
	    	if (array1[array1off+i] != array2[array2off+i])
	    		return false;
	    return true;
	}
	
	/**
	 * transfer one byte into one int
	 * @param b : source byte array
	 * @param off : the position of operating byte 
	 * @return
	 */
	public static int onebyte2int(byte[] b, int off)
	{
		return (0xFF & b[off]);
	}
	
	/**
	 * transfer one int into one byte
	 * @param n : source integer
	 * @param b : destination byte array
	 * @param off : the position of operating byte 
	 * @throws Exception
	 */
	public static void int2onebyte(int n, byte[] b, int off) throws Exception
	{
		//if(!(n>=0 && n<=255)) throw new Exception(n+" exceed unsigned char range [0,255]!");
		b[off] = 0x00;
		b[off] |= n;
	}
	
	/**
	 * transfer two bytes into one int
	 * @param b : source byte array
	 * @param off : the position of operating byte 
	 * @return
	 */
	public static int twobyte2int(byte[] b, int off)
	{
		int n = 0;
		n |= ( 0xFF & b[off] ) << 8;
		n |= ( 0xFF & b[off+1] );
		return n;
	}
	
	/**
	 * transfer one int into two bytes
	 * @param n : source integer
	 * @param b : destination byte array
	 * @param off : the position of operating byte 
	 * @throws Exception
	 */
	public static void int2twobyte(int n, byte[] b, int off) throws Exception
	{
		if(!(n>=0 && n<=65535)) throw new Exception(n+" exceed unsigned short int range [0,65535]!");
		b[off]   = 0x00;
		b[off+1] = 0x00;
		b[off]   |= (n >> 8);
		b[off+1] |= n;
	}
	
	public static int threebyte2int(byte[] b, int off)
	{
		int n = 0;
		n |= ( 0xFF & b[off] )   << 16;
		n |= ( 0xFF & b[off+1] ) << 8;
		n |= ( 0xFF & b[off+2] );
		return n;
	}
	
	public static void int2threebyte(int n, byte[] b, int off) throws Exception
	{
		if(!(n>=0 && n<=0xFFFFFF)) throw new Exception(n+" exceed unsigned short int range [0,16777215]!");
		b[off]   = 0x00;
		b[off+1] = 0x00;
		b[off+2] = 0x00;
		b[off]   |= (n >> 16);
		b[off+1] |= (n >> 8);
		b[off+2] |= n;
	}
	
	/**
	 * transfer four bytes into one long
	 * @param b : source byte array
	 * @param off : the position of operating byte 
	 * @return
	 */
	public static long fourbyte2long(byte[] b, int off)
	{
		long n = 0L;
		n |= ( 0xFFL & b[off] )   << 24;
		n |= ( 0xFFL & b[off+1] ) << 16;
		n |= ( 0xFFL & b[off+2] ) << 8;
		n |= ( 0xFFL & b[off+3] );
		return n;
	}
	
	/**
	 * transfer one long into four bytes
	 * @param n : source integer
	 * @param b : destination byte array
	 * @param off : the position of operating byte 
	 * @throws Exception
	 */
	public static void long2fourbyte(long n, byte[] b, int off) throws Exception
	{
		if(!(n>=0 && n<=4294967295L)) throw new Exception(n+" exceed unsigned long int range [0,4294967295]!");
		b[off]   = 0x00;
		b[off+1] = 0x00;
		b[off+2] = 0x00;
		b[off+3] = 0x00;
		b[off]   |= (n >> 24);
		b[off+1] |= (n >> 16);
		b[off+2] |= (n >> 8);
		b[off+3] |= n;
	}
	
	public static String toHexString(byte[] byte_array)
	{
		if(byte_array==null) return "";
		int viewLength = 16;
		StringBuilder sb = new StringBuilder();
		String left_bar = null;
		left_bar = "*--*";
		sb.append(StringUtil.padStringWithFixedLength(left_bar, 10));
		for(int i=0; i<viewLength; ++i)
		{
			sb.append('*');
			sb.append('0');
			sb.append( hex_chars[ i ] );
			sb.append('*');
			sb.append(' ');
		}
		sb.append('\n');
		for(int i=0; i<byte_array.length; ++i)
		{
			if( i%viewLength==0 )
			{
				if(i>0) sb.append('\n');
				int row = i / viewLength;
				left_bar = "*"+(row+1)+"*";
				sb.append(StringUtil.padStringWithFixedLength(left_bar, 10));
			}
			byte temp = byte_array[i];
			sb.append('[');
			sb.append( hex_chars[ temp>>>4 & 0X0F ] );
			sb.append( hex_chars[ temp & 0X0F ] );
			sb.append(']');
			sb.append(' ');
		}
		return sb.toString();
	}
	
	public byte[] subArray(byte[] byte_array,int off,int len)
	{
		byte[] result = new byte[len];
		System.arraycopy(byte_array, off, result, 0, len);
		return result;
	}
	
	public static String byte2hex(byte[] byte_array,int off,int len)
	{
		StringBuilder sb = new StringBuilder();
		for(int i=off; i<off+len; ++i)
		{
			byte temp = byte_array[i];
			sb.append( hex_chars[ temp>>>4 & 0X0F ] );
			sb.append( hex_chars[ temp & 0X0F ] );
		}
		return sb.toString();
	}
	
	public static String byte2hex(byte[] byte_array)
	{
		StringBuilder sb = new StringBuilder();
		for(int i=0; i<byte_array.length; ++i)
		{
			byte temp = byte_array[i];
			sb.append( hex_chars[ temp>>>4 & 0X0F ] );
			sb.append( hex_chars[ temp & 0X0F ] );
		}
		return sb.toString();
	}
	
	public static String byte2hexWithBlank(byte[] byte_array)
	{
		if(byte_array==null) return "";
		StringBuilder sb = new StringBuilder();
		for(int i=0; i<byte_array.length; ++i)
		{
			byte temp = byte_array[i];
			sb.append( hex_chars[ temp>>>4 & 0X0F ] );
			sb.append( hex_chars[ temp & 0X0F ] );
			sb.append(" ");
		}
		return sb.toString();
	}
	
	public static byte[] hex2byte(String hexString) throws Exception
	{
		if(hexString==null) return null;
		hexString = hexString.trim();
		byte[] hexStrBytes = hexString.getBytes();
		if(hexStrBytes.length % 2 != 0) throw new IllegalArgumentException("["+hexString+"] lengh is even");
		byte[] bytes = new byte[hexStrBytes.length/2];
		for(int i=0; i<hexStrBytes.length; i+=2 )
		{
			String item = new String(hexStrBytes,i,2);
			int2onebyte(Integer.parseInt(item,16),bytes,i/2); 
			//bytes[i/2] = (byte)Integer.parseInt(item,16);
		}
		return bytes;
	}
	
	public static byte[] hex2byteWithBlank(String hexStringWithBlank) throws Exception
	{
		hexStringWithBlank = hexStringWithBlank.replaceAll(" ", "");
		return hex2byte(hexStringWithBlank);
	}
	
	public static boolean equals(byte[] b1,byte[] b2)
	{
		return Arrays.equals(b1, b2);
	}
	
	public static String revertStringFromBytes(byte[] ba)
	{
		int index = 0;
		for(int i=0; i<ba.length; ++i)
		{
			if(ba[i]=='\0')
			{
				index = i;
				break;
			}
		}
		return new String(ba,0,index);
	}
	
}
